import requests
from bs4 import BeautifulSoup
import pandas as pd
import datetime
import schedule
import time
import os
import configparser
import logging
from logging.handlers import RotatingFileHandler

# 版本信息
__version__ = "1.1.0"

# 读取配置文件
def load_config():
    """加载配置文件，如果不存在则创建默认配置"""
    config = configparser.ConfigParser()
    
    # 默认配置
    default_config = {
        'Settings': {
            'fetch_interval_minutes': '1',
            'data_save_path': 'gold_data',
            'data_source_url': 'https://www.jinjia.com.cn/'
        },
        'Logging': {
            'log_level': 'INFO',
            'log_file': 'gold_tracker.log',
            'log_max_size': '5242880',  # 5MB
            'log_backup_count': '3'
        }
    }
    
    # 如果配置文件存在，读取配置
    if os.path.exists('config.ini'):
        config.read('config.ini', encoding='utf-8')
    else:
        # 否则使用默认配置
        for section, options in default_config.items():
            if not config.has_section(section):
                config.add_section(section)
            for option, value in options.items():
                config.set(section, option, value)
        
        # 保存默认配置到文件
        with open('config.ini', 'w', encoding='utf-8') as f:
            config.write(f)
    
    return config

# 配置日志
def setup_logging(config):
    """配置日志记录器"""
    log_file = config.get('Logging', 'log_file')
    log_level_str = config.get('Logging', 'log_level')
    log_max_size = config.getint('Logging', 'log_max_size')
    log_backup_count = config.getint('Logging', 'log_backup_count')
    
    # 将日志级别字符串转换为常量
    log_level = getattr(logging, log_level_str.upper(), logging.INFO)
    
    # 创建记录器
    logger = logging.getLogger('gold_tracker')
    logger.setLevel(log_level)
    
    # 清空现有的处理器
    for handler in logger.handlers[:]:
        logger.removeHandler(handler)
    
    # 创建控制台处理器
    console_handler = logging.StreamHandler()
    console_handler.setLevel(log_level)
    console_format = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    console_handler.setFormatter(console_format)
    
    # 创建文件处理器
    file_handler = RotatingFileHandler(
        log_file, 
        maxBytes=log_max_size, 
        backupCount=log_backup_count,
        encoding='utf-8'
    )
    file_handler.setLevel(log_level)
    file_format = logging.Formatter('%(asctime)s - %(levelname)s - %(funcName)s - %(message)s')
    file_handler.setFormatter(file_format)
    
    # 添加处理器到记录器
    logger.addHandler(console_handler)
    logger.addHandler(file_handler)
    
    return logger

def get_gold_price(config, logger):
    """获取金价数据"""
    url = config.get('Settings', 'data_source_url')
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
    }
    
    try:
        logger.info(f"开始获取金价数据 - URL: {url}")
        response = requests.get(url, headers=headers)
        response.encoding = 'utf-8'  # 设置正确的编码
        response.raise_for_status()
        
        # 使用BeautifulSoup解析HTML
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # 创建数据字典
        data = {
            "时间": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "国内金价": [],
            "国际金价": [],
            "银行金价": []
        }
        
        # 获取主列表中的金价数据（国内黄金价格）
        gold_list = soup.select(".mian .left .bg .list ul li")
        if gold_list:
            logger.debug(f"找到国内金价列表项: {len(gold_list)-1}个")
            # 跳过标题行
            for item in gold_list[1:]:
                try:
                    name_element = item.select_one(".name a")
                    price_element = item.select_one(".new span")
                    change_element = item.select_one(".rise span")
                    open_element = item.select_one(".open")
                    prev_close_element = item.select_one(".prec")
                    update_time_element = item.select_one(".time")
                    
                    if name_element and price_element:
                        gold_data = {
                            "种类": name_element.get_text(strip=True),
                            "价格": price_element.get_text(strip=True),
                            "涨跌": change_element.get_text(strip=True) if change_element else "",
                            "开盘价": open_element.get_text(strip=True) if open_element else "",
                            "昨收价": prev_close_element.get_text(strip=True) if prev_close_element else "",
                            "更新时间": update_time_element.get_text(strip=True) if update_time_element else ""
                        }
                        data["国内金价"].append(gold_data)
                        logger.debug(f"解析国内金价: {gold_data['种类']} - {gold_data['价格']}")
                except Exception as e:
                    logger.error(f"解析国内金价条目出错: {e}")
        else:
            logger.warning("未找到国内金价数据")
        
        # 获取国际金价数据
        try:
            # 直接访问国际金价专页获取更完整数据
            intl_url = url + "gjgold/"
            logger.info(f"获取国际金价数据 - URL: {intl_url}")
            intl_response = requests.get(intl_url, headers=headers)
            intl_response.encoding = 'utf-8'  # 设置正确的编码
            intl_soup = BeautifulSoup(intl_response.text, 'html.parser')
            
            intl_items = intl_soup.select(".mian .left .bg .list ul li")
            if intl_items:
                logger.debug(f"找到国际金价列表项: {len(intl_items)-1}个")
                # 跳过标题行
                for item in intl_items[1:]:
                    try:
                        name_element = item.select_one(".name a")
                        price_element = item.select_one(".new span")
                        change_element = item.select_one(".rise span")
                        
                        if name_element and price_element:
                            gold_data = {
                                "种类": name_element.get_text(strip=True),
                                "价格": price_element.get_text(strip=True),
                                "涨跌": change_element.get_text(strip=True) if change_element else ""
                            }
                            data["国际金价"].append(gold_data)
                            logger.debug(f"解析国际金价: {gold_data['种类']} - {gold_data['价格']}")
                    except Exception as e:
                        logger.error(f"解析国际金价条目出错: {e}")
            else:
                logger.warning("未找到国际金价数据")
        except Exception as e:
            logger.error(f"获取国际金价页面出错: {e}")
        
        # 获取银行金价
        try:
            # 访问银行金价专页
            bank_url = url + "yhgold/"
            logger.info(f"获取银行金价数据 - URL: {bank_url}")
            bank_response = requests.get(bank_url, headers=headers)
            bank_response.encoding = 'utf-8'  # 设置正确的编码
            bank_soup = BeautifulSoup(bank_response.text, 'html.parser')
            
            bank_items = bank_soup.select(".mian .left .bg .list ul li")
            if bank_items:
                logger.debug(f"找到银行金价列表项: {len(bank_items)-1}个")
                # 跳过标题行
                for item in bank_items[1:]:
                    try:
                        name_element = item.select_one(".name a")
                        buy_element = item.select_one(".new span")
                        sell_element = item.select_one(".sell")
                        
                        if name_element and buy_element:
                            bank_data = {
                                "银行": name_element.get_text(strip=True),
                                "买入价": buy_element.get_text(strip=True),
                                "卖出价": sell_element.get_text(strip=True) if sell_element else ""
                            }
                            data["银行金价"].append(bank_data)
                            logger.debug(f"解析银行金价: {bank_data['银行']} - {bank_data['买入价']}")
                    except Exception as e:
                        logger.error(f"解析银行金价条目出错: {e}")
            else:
                logger.warning("未找到银行金价数据")
        except Exception as e:
            logger.error(f"获取银行金价页面出错: {e}")
        
        # 统计数据
        total_items = len(data["国内金价"]) + len(data["国际金价"]) + len(data["银行金价"])
        logger.info(f"成功获取金价数据，共{total_items}条 (国内金价:{len(data['国内金价'])}条, 国际金价:{len(data['国际金价'])}条, 银行金价:{len(data['银行金价'])}条)")
        return data
    
    except Exception as e:
        logger.error(f"获取金价数据失败: {e}")
        return None

def save_to_excel(data, config, logger):
    """保存数据到Excel文件"""
    if not data:
        logger.warning("没有数据可保存")
        return
    
    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"gold_price_{timestamp}.xlsx"
    data_save_path = config.get('Settings', 'data_save_path')
    
    # 确保输出目录存在
    os.makedirs(data_save_path, exist_ok=True)
    output_path = os.path.join(data_save_path, filename)
    
    try:
        logger.info(f"开始保存数据到Excel文件: {output_path}")
        
        # 创建Excel写入器
        with pd.ExcelWriter(output_path) as writer:
            # 国内金价表
            if data["国内金价"]:
                domestic_df = pd.DataFrame(data["国内金价"])
                domestic_df.to_excel(writer, sheet_name="国内金价", index=False)
                logger.debug(f"保存国内金价数据: {len(data['国内金价'])}条")
            
            # 国际金价表
            if data["国际金价"]:
                intl_df = pd.DataFrame(data["国际金价"])
                intl_df.to_excel(writer, sheet_name="国际金价", index=False)
                logger.debug(f"保存国际金价数据: {len(data['国际金价'])}条")
            
            # 银行金价表
            if data["银行金价"]:
                bank_df = pd.DataFrame(data["银行金价"])
                bank_df.to_excel(writer, sheet_name="银行金价", index=False)
                logger.debug(f"保存银行金价数据: {len(data['银行金价'])}条")
            
            # 汇总信息表
            summary = pd.DataFrame({
                "数据时间": [data["时间"]],
                "数据来源": ["金价网 (www.jinjia.com.cn)"],
                "数据项": [f"国内金价({len(data['国内金价'])})、国际金价({len(data['国际金价'])})、银行金价({len(data['银行金价'])})"],
                "程序版本": [__version__]
            })
            summary.to_excel(writer, sheet_name="数据信息", index=False)
        
        logger.info(f"金价数据已成功保存至 {output_path}")
        return output_path
    
    except Exception as e:
        logger.error(f"保存Excel文件失败: {e}")
        return None

def job(config, logger):
    """定时任务，获取数据并保存"""
    logger.info(f"开始执行定时任务: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    data = get_gold_price(config, logger)
    if data:
        save_to_excel(data, config, logger)
    else:
        logger.warning("本次任务未获取到数据，跳过保存")

def main():
    """主函数"""
    # 加载配置
    config = load_config()
    
    # 设置日志
    logger = setup_logging(config)
    
    # 输出版本信息
    logger.info(f"金价监控工具 v{__version__} 已启动")
    logger.info(f"配置文件: {os.path.abspath('config.ini')}")
    
    # 读取配置
    fetch_interval = config.getint('Settings', 'fetch_interval_minutes')
    data_save_path = config.get('Settings', 'data_save_path')
    
    logger.info(f"数据获取间隔: {fetch_interval}分钟")
    logger.info(f"数据保存路径: {os.path.abspath(data_save_path)}")
    
    # 立即运行一次
    logger.info("正在获取初始数据...")
    job(config, logger)
    
    # 设置定时任务
    schedule.every(fetch_interval).minutes.do(job, config=config, logger=logger)
    
    logger.info(f"已设置每{fetch_interval}分钟自动获取金价数据")
    logger.info("按 Ctrl+C 终止程序")
    
    try:
        while True:
            schedule.run_pending()
            time.sleep(1)
    except KeyboardInterrupt:
        logger.info("用户终止程序")
    except Exception as e:
        logger.critical(f"程序发生严重错误: {e}")
    finally:
        logger.info("程序已退出")

if __name__ == "__main__":
    main() 